﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO;
using System.Net;
using Common;
using Site_Extractor;
using KnowFirst.Properties;
using Searching;
using Indexing;
using Server;
using Dal;

namespace KnowFirst
{
	public partial class KnowFirstGUI : Form
	{
		int _updateInterval=1200*1000;
		bool _updateTimerIsWorking, _updateNowIsWorking;
		Timer _updateTimer;
		DateTime _searchBegin;
		BackgroundWorker _updateBW, _searchBW;
		Action<string> _reportStatus;
        Action<string> _displayResult;
        Server.Server _server;
		Searching.Searcher _searcher;
		List<Site> _sites;
		bool _searching;
        public event EventHandler<Notifying.NotifyEventArgs> NotifyEvent;
        object _updateLock = new object();
        Indexer _indexer;
		List<GenericForum> _forums;

        public KnowFirstGUI()
        {
            InitializeComponent();
            _reportStatus = new Action<string>(_ReportStatus);
            _displayResult = new Action<string>(_DisplayResults);
            _updateTimer = new Timer();
            _updateTimer.Interval = _updateInterval;
            _updateTimer.Tick += new EventHandler(_updateTimer_Tick);
            _indexer = new Indexer(new Catalog( Settings.Default.PathToPrefixFile), Settings.Default.ConnectionString,
                Settings.Default.PathToPrefixFile, 2);
            
            UpdateDatabaseInfo();
            _searcher = new Searching.Searcher();
            LoadAllSites();
			LoadAllForums();
            //var sites = XmlClassHandler<Site[]>.Load(Settings.Default.PathToSitesXML);
            _server = new Server.Server(new List<Site>(_sites), _forums, Settings.Default.ConnectionString,
             Settings.Default.PathToPrefixFile);
            _server._notifier.NotificationEvent += new EventHandler<Notifying.NotifyEventArgs>(onNotifyingEvent);
            

        }

        public void LoadAllSites()
        {
            //Load from sites.xml
            //var sites = XmlClassHandler<Site[]>.Load(Settings.Default.PathToSitesXML);
            _sites = new List<Site>();
            //Load from Sites directory.
            string [] fileEntries = Directory.GetFiles(Settings.Default.SiteXMLDirectory,
                "*", SearchOption.TopDirectoryOnly);
            
            foreach (string fileName in fileEntries)
            {
                var site = XmlClassHandler<Site>.Load(fileName);
                _sites.Add(site);
            }
            Dal.DatabaseAbstractionLayer dal = new DatabaseAbstractionLayer(""); //no con string needed.
            dal.UpdateSites(_sites);
        }

		public void LoadAllForums()
		{
			_forums = new List<GenericForum>();
			string[] fileEntries = Directory.GetFiles(Settings.Default.ForumsXmlDirectory,
				"*", SearchOption.TopDirectoryOnly);
			foreach (string filename in fileEntries)
			{
				var f = GenericForum.LoadFromXml(filename);
				_forums.Add(f);
			}
			// TODO: DAL part ?
		}


        public void onNotifyingEvent(Object sender, Notifying.NotifyEventArgs args)
        {
            args.Documents = Document.SortDocumentsByPriorityDate(args.Documents);
            foreach (Document doc in args.Documents)
            {
                DisplayResult(args.User.UserName + " " + doc.Fields[0].Text);
            }
        }

		#region RSS Stuff

		private void RssTimerWorking(bool on)
		{
			updateStartButton.Enabled = !on;
			updateStopButton.Enabled = on;
			rssLinkTextBox.Enabled = !on;
		}
		private void updateStart()
		{
            UpdateNow();
            updateTimerSet(true);
            
			
		}

		void _updateTimer_Tick(object sender, EventArgs e)
		{
           
                UpdateNow();
		}
		private void RssStop() 
		{
           
			updateTimerSet(false);
		}

		private void updateTimerSet(bool on)
		{
			ReportStatus(string.Format("Timer set to {0}", (on?"ON":"OFF")));
			if (on)
			{
				if (_updateTimerIsWorking) return;
				_updateTimer.Interval = _updateInterval; // maybe reset the timer ?
				_updateTimer.Start();
				RssTimerWorking(true);
				_updateTimerIsWorking = true;
			}
			else // on == false
			{
				if (!_updateTimerIsWorking) return;
				_updateTimer.Stop();
				RssTimerWorking(false);
				_updateTimerIsWorking = false;
			}
		}
		private void UpdateNow()
		{
			//if (_updateNowIsWorking) return;
            ReportStatus("Starting Update...");
			_updateNowIsWorking = true;
			updateNowButton.Enabled = false;

			
            
			_updateBW = new BackgroundWorker();
            _updateBW.WorkerReportsProgress = true;
            _updateBW.WorkerSupportsCancellation = false;
            _updateBW.RunWorkerCompleted += new RunWorkerCompletedEventHandler(_updateBW_RunWorkerCompleted);
            _updateBW.DoWork += new DoWorkEventHandler(_updateBW_DoWork);
            _updateBW.ProgressChanged += new ProgressChangedEventHandler(_updateBW_ProgressChanged);
            _updateBW.RunWorkerAsync();

			
		}

		void _updateBW_ProgressChanged(object sender, ProgressChangedEventArgs e)
		{
			rssProgressBar.Value = e.ProgressPercentage;
			UpdateDatabaseInfo();
		}

        void _updateBW_DoWork(object sender, DoWorkEventArgs e)
        {
            int totalSites, currentSite;

            //ReportStatus("Update in progress... waiting for lock...");
            lock (_updateLock) //Locks the update if timer has fired before it is complete.
            {
                //ReportStatus("performing update");
                _server.DoUpdateCycle(DateTime.Now.Subtract(new TimeSpan(168,0,0)));
            }
        }

		void _updateBW_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
		{
			var links = e.Result as RssLink[];
			ReportStatus(string.Format("Update Done."));
			updateNowButton.Enabled = true;
			_updateNowIsWorking = false;
		}

		private void updateStartButton_Click(object sender, EventArgs e)
		{
			updateStart();
		}

		private void updateStopButton_Click(object sender, EventArgs e)
		{
			RssStop();
		}

		private void updateNowButton_Click(object sender, EventArgs e)
		{
			UpdateNow();
		}

		#endregion

		#region Status

        private void _DisplayResults(string result)
        {
            searchListBox.Items.Add(result);
        }
		private void _ReportStatus(string status)
		{ 
			statusTextBox.AppendText(string.Format("{0}: {1}\r\n",DateTime.Now.ToString(), status));
		}
		private void ReportStatus(string status)
		{
			Invoke(_reportStatus, status);
		}
		private void ReportError(string error)
		{
			Invoke(_reportStatus, string.Format("Error: {0}", error));
		}

        private void DisplayResult(string result)
        {
            Invoke(_displayResult, result);
        }

		#endregion
        
		private Document[] RunSearch(string term)
		{
			try
			{
				ReportStatus(string.Format("Running search for {0}", term));
				List<Document> docs = new List<Document>();
                docs = _searcher.DoSearch(term, _indexer.Catalog);//, new User(0,"","","","",new List<UserQuery>()));
				return docs.ToArray();
			}
			catch (Exception ex) { ReportError(ex.Message); return null; }
          
		}
         
		private void PutResults(Document[] docs)
		{
			searchListBox.Items.Clear();
			foreach (Document doc in docs) searchListBox.Items.Add(doc);
		}

		private void searchTextBox_TextChanged(object sender, EventArgs e)
		{
			SearchButtonEnforce();
		}

		private void SearchButtonEnforce()
		{
			searchButton.Enabled = (searchTextBox.Text != string.Empty) && !_searching;
		}

		private void searchButton_Click(object sender, EventArgs e)
		{
			searchButton.Enabled = false;

			_searchBW = new BackgroundWorker();
			_searchBW.WorkerReportsProgress = false;
			_searchBW.WorkerSupportsCancellation = false;
			_searchBW.RunWorkerCompleted += new RunWorkerCompletedEventHandler(_searchBW_RunWorkerCompleted);
			_searchBW.DoWork += new DoWorkEventHandler(_searchBW_DoWork);
			_searchBW.RunWorkerAsync(searchTextBox.Text);

			selectTextBox.Text = searchTextBox.Text;
			searchTextBox.Text = string.Empty;
            string term = searchTextBox.Text as string;
			_searchBegin = DateTime.Now;
            var results = RunSearch(term);
			SearchDone(results);
		}

		void _searchBW_DoWork(object sender, DoWorkEventArgs e)
		{
			string term = e.Argument as string;
			var results = RunSearch(term);
			e.Result = results;
		}

		void SearchDone(Document[] docs)
		{

			TimeSpan time = DateTime.Now - _searchBegin;
			if (docs == null) { ReportStatus("(null results)"); docs = new Document[0]; }
			var results = docs;
			ReportStatus(string.Format("Search done, found {0} result(s)", results.Length));
			if (_searchBegin != null) 
			{
				ReportStatus(string.Format("Search took {0:D2}:{1:D2}:{2:D3}", time.Minutes, time.Seconds, time.Milliseconds));
			}
			PutResults(results);
			SearchButtonEnforce();
		}

		void _searchBW_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
		{
			var results = e.Result as Document[];
			SearchDone(results);
		}

		private void searchListBox_SelectedIndexChanged(object sender, EventArgs e)
		{
			object item = searchListBox.SelectedItem;
			if (item == null || !(item is Document)) return;
			var doc = item as Document;
			docTitleRichTextBox.Text = doc.Fields[(int)Document.Section.Title].Text;
            docSummaryRichTextBox.Text = doc.Fields[(int)Document.Section.Summary].Text;
            docTextRichTextBox.Text = doc.Fields[(int)Document.Section.Text].Text;
            docTimeRichTextBox.Text = doc.DateTime.ToString();
			if (selectTextBox.Text == string.Empty) return;
			SelectTermInRichTextBoxes(selectTextBox.Text);
		}

		private void MarkSelectedWord(string term, RichTextBox tb)
		{			
			int i = 0;
			i = tb.Find(term, i, RichTextBoxFinds.None);
			while (i != -1)
			{
				tb.Select(i, term.Length);
				tb.SelectionColor = Color.White;
				tb.SelectionBackColor = Color.Blue;
				i = tb.Find(term, i + term.Length, RichTextBoxFinds.None);
			}
		}

		private void SelectTermInRichTextBoxes(string term)
		{
			foreach (Control c in splitContainer1.Panel1.Controls)
			{
				if (!(c is RichTextBox)) continue;
				var tb = c as RichTextBox;
				MarkSelectedWord(term, tb);
			}
		}

		private void selectButton_Click(object sender, EventArgs e)
		{
			SelectTermInRichTextBoxes(selectTextBox.Text);
		}

		private void _UpdateDatabaseInfo()
		{
			int docs = _indexer.Catalog.NumberOfDocumentsIndexed;			
			infoLabel.Text = string.Format("{0} document(s)", docs);
		}
		private void UpdateDatabaseInfo()
		{
			try
			{
				Invoke(new Action(_UpdateDatabaseInfo));
			}
			catch { _UpdateDatabaseInfo(); }
		}

		private void KnowFirstGUI_FormClosing(object sender, FormClosingEventArgs e)
		{
			
		}

        private void button1_Click(object sender, EventArgs e)
        {
            _indexer.LoadDbToCatalog();

        }

	}        
}
